/*!
    \copyright  Copyright (c) 2019 - 2024 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \ingroup    device_test_service
    \addtogroup device_test_service
    @{
    \brief      Implementation of the auth commands for device test service
*/


#include "device_test_service.h"
#include "device_test_service_auth.h"
#include "device_test_parse.h"
#include "device_test_service_data.h"
#include "device_test_service_commands_helper.h"

#include <anc.h>
#include <anc_state_manager.h>

#include <logging.h>
#include <stdio.h>
#include <ctype.h>

#ifdef ENABLE_ANC

/*! \name Range of ANC mode value supported in the AT command.

    These are mapped onto the equivalent anc_mode_x enumerated
    type.
*/
#define MIN_AT_ANC_MODE     1
#define MAX_AT_ANC_MODE     10

/*! \name Mapping of gain_path parameters in the AT commands to
    defines used by the ANC library
*/
#define FFA_PATH    1   /*!< Feed Forward path A */
#define FFB_PATH    2   /*!< Feed Forward path B */
#define FB_PATH     3    /*!< Feed Back path */

/*! Response message sent to the read fine gain command */
#define ANC_READ_FINE_GAIN_RESPONSE "+ANCREADFINEGAIN:"
/*! Maximum length of the response message, allowing for an 8
    bit value (max '255' - 3 digits) */
#define FULL_FINE_GAIN_RESPONSE_LEN (sizeof(ANC_READ_FINE_GAIN_RESPONSE) + 3)

/*! Response message sent to the read coarse gain command */
#define ANC_READ_COARSE_GAIN_RESPONSE "+ANCREADCOARSEGAIN:"
/*! Maximum length of the response message*/
#define FULL_COARSE_GAIN_RESPONSE_LEN (sizeof(ANC_READ_COARSE_GAIN_RESPONSE) + 6)

/*! Response message sent to the read fine gain command for both ANC instances*/
/* 3 digits for instance0 gain, 3 digits for instance1 gain. Plus 1 to accomodate the space in sprintf between two instance gains*/
#define ANC_READ_FINE_GAIN_DUAL_RESPONSE  "+ANCREADFINEGAINDUAL:"
#define FULL_FINE_GAIN_DUAL_RESPONSE_LEN (sizeof(ANC_READ_FINE_GAIN_DUAL_RESPONSE) + 7)


/*! \brief Map the ANC mode supplied in the AT command to a mode understood by libraries 

    \param requested_mode   The mode value from the AT command
    \param[out] anc_mode    Pointer to location to take the library mode value

    \return TRUE if the value passed was legal, and value could be mapped. 
            FALSE otherwise
*/
static bool deviceTestService_AncMapMode(uint16 requested_mode, anc_mode_t* anc_mode)
{
    if (MIN_AT_ANC_MODE <= requested_mode && requested_mode <= MAX_AT_ANC_MODE)
    {
        *anc_mode = (anc_mode_t)(requested_mode - MIN_AT_ANC_MODE + anc_mode_1);
        return TRUE;
    }
    DEBUG_LOG_WARN("deviceTestService_AncMapMode Attempt to map a bad ANC mode:%d",requested_mode);
    return FALSE;
}

/*! \brief Map the gain path supplied in the AT command to a mode understood by libraries 

    \param requested_gain_path   The gain_path value from the AT command
    \param[out] gain_path        Pointer to location to take the library gain_path value

    \return TRUE if the value passed was legal, and value could be mapped. 
            FALSE otherwise
*/
static bool deviceTestService_AncMapGainPath(uint16 requested_gain_path, 
                                           audio_anc_path_id *gain_path)
{
    switch (requested_gain_path)
    {
        case FFA_PATH:
            *gain_path = AUDIO_ANC_PATH_ID_FFA;
            break;

        case FFB_PATH:
            *gain_path = AUDIO_ANC_PATH_ID_FFB;
            break;

        case FB_PATH:
            *gain_path = AUDIO_ANC_PATH_ID_FB;
            break;

        default:
            DEBUG_LOG_WARN("deviceTestService_AncMapGainPath. Attempt to map a bad gain path:%d", requested_gain_path);
            return FALSE;
    }

    return TRUE;
}


static bool deviceTestService_AncCommandsAllowed(void)
{
    return DeviceTestService_CommandsAllowed() && AncStateManager_IsSupported();
}


/*! \brief Handle the AT+ANCENABLE command

    Handle the enable, making sure to send either an "OK" or "ERROR" response.

    \note ANC Enable needs the audio sub system to be started, which can
            cause a delay before the OK response is sent.

    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] ancEnable Structure supplied by the AT command parser, providing
                    the parameters passed in the AT command (the requested mode to
                    enable)
*/
void DeviceTestServiceCommand_HandleAncEnable(Task task, 
                const struct DeviceTestServiceCommand_HandleAncEnable *ancEnable)
{
    anc_mode_t anc_mode;
    uint8 gain;

    DEBUG_LOG_ALWAYS("DeviceTestServiceCommand_HandleAncEnable. mode:%d", ancEnable->mode);

    if (   !deviceTestService_AncCommandsAllowed()
        || !deviceTestService_AncMapMode(ancEnable->mode, &anc_mode))
    {
        DeviceTestService_CommandResponseError(task);
        return;
    }

    AncStateManager_EnableForDts(anc_mode);

    /* The return value from AncSetMode does not indicate whether the mode
       set was successful, so attempt a read - which will return ERROR 
       if the mode was invalid mode */
    DeviceTestService_CommandResponseOkOrError(task, 
                            AncReadFineGain(AUDIO_ANC_PATH_ID_FFA, &gain));
}


/*! \brief Handle the AT+ANCDISABLE command

    Handle the disable, making sure to send either an "OK" or "ERROR" response.

    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
*/
void DeviceTestServiceCommand_HandleAncDisable(Task task)
{
    DEBUG_LOG_ALWAYS("DeviceTestServiceCommand_HandleAncDisable");
    
    if (!deviceTestService_AncCommandsAllowed())
    {
        DeviceTestService_CommandResponseError(task);
        return;
    }

    AncStateManager_DisableForDts();

    DeviceTestService_CommandResponseOk(task);
}


/*! \brief Handle the AT+ANCSETFINEGAIN command

    Handle the command, making sure to send either an "OK" or "ERROR" response.

    Parameters are checked, any mistakes causing an error response.

    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] ancSetFineGain Structure supplied by the AT command parser, providing
                              the parameters passed in the AT command (the requested 
                              mode, gain path and gain value)
*/
void DeviceTestServiceCommand_HandleAncSetFineGain(Task task, 
                    const struct DeviceTestServiceCommand_HandleAncSetFineGain *ancSetFineGain)
{
    anc_mode_t anc_mode;
    audio_anc_path_id gain_path;
    bool status_instance_0 = FALSE;
#ifdef ENABLE_ENHANCED_ANC
    bool status_instance_1 = FALSE;
#else
    bool status_instance_1 = TRUE;
#endif

    DEBUG_LOG_ALWAYS("DeviceTestServiceCommand_HandleAncSetFindGain  mode:%u Path:%u Gain:%u ",
                     ancSetFineGain->mode,
                     ancSetFineGain->gainpath, ancSetFineGain->gainvalue);

    if (   !deviceTestService_AncCommandsAllowed()
        || !AncStateManager_IsEnabled()
        || !deviceTestService_AncMapMode(ancSetFineGain->mode, &anc_mode)
        || !deviceTestService_AncMapGainPath(ancSetFineGain->gainpath, &gain_path)
        || ancSetFineGain->gainvalue > 0xFF)
    {
        DeviceTestService_CommandResponseError(task);
        return;
    }

    /*Set gain for currently set mode in DSP*/
    switch(ancSetFineGain->gainpath)
    {
        case FFA_PATH:
            status_instance_0 = AncConfigureFFAPathGain(AUDIO_ANC_INSTANCE_0,
                                             (uint8)ancSetFineGain->gainvalue);

#ifdef ENABLE_ENHANCED_ANC
            status_instance_1 = AncConfigureFFAPathGain(AUDIO_ANC_INSTANCE_1,
                                             (uint8)ancSetFineGain->gainvalue);
#endif
        break;

        case FFB_PATH:
            status_instance_0 = AncConfigureFFBPathGain(AUDIO_ANC_INSTANCE_0,
                                             (uint8)ancSetFineGain->gainvalue);


#ifdef ENABLE_ENHANCED_ANC
            status_instance_1 = AncConfigureFFBPathGain(AUDIO_ANC_INSTANCE_1,
                                             (uint8)ancSetFineGain->gainvalue);
#endif
            break;

        case FB_PATH:
            status_instance_0 = AncConfigureFBPathGain(AUDIO_ANC_INSTANCE_0,
                                            (uint8)ancSetFineGain->gainvalue);

#ifdef ENABLE_ENHANCED_ANC
            status_instance_1 = AncConfigureFBPathGain(AUDIO_ANC_INSTANCE_1,
                                            (uint8)ancSetFineGain->gainvalue);
#endif
            break;

        default:
            /* Panic here as the mapping should have ensured a valid gain_path
               A Panic would indicate a new gain_path should be supported but 
               has not been implemented here */
            Panic();
            break;
    }

    DeviceTestService_CommandResponseOkOrError(task, status_instance_0 && status_instance_1);
}


/*! \brief Handle the AT+ANCREADFINEGAIN command

    Handle the command, making sure to send either an "OK" or "ERROR" response.

    Parameters are checked, any mistakes causing an error response.

    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] ancReadFineGain Structure supplied by the AT command parser, providing
                               the parameters passed in the AT command (the requested 
                               mode and gain path)
*/
void DeviceTestServiceCommand_HandleAncReadFineGain(Task task, 
                const struct DeviceTestServiceCommand_HandleAncReadFineGain *ancReadFineGain)
{
    anc_mode_t anc_mode;
    audio_anc_path_id gain_path;
    uint8 gain=0;
    char response[FULL_FINE_GAIN_RESPONSE_LEN];

    DEBUG_LOG_ALWAYS("DeviceTestServiceCommand_HandleAncReadFineGain mode:%u Path:%u ",
                     ancReadFineGain->mode, ancReadFineGain->gainpath);

    if (   !deviceTestService_AncCommandsAllowed()
        || !AncStateManager_IsEnabled()
        || !deviceTestService_AncMapMode(ancReadFineGain->mode, &anc_mode)
        || !deviceTestService_AncMapGainPath(ancReadFineGain->gainpath, &gain_path))
    {
        DeviceTestService_CommandResponseError(task);
        return;
    }

    /*This ANC interface makes sure to return the gain which golden config plus any delta from earlier calibration*/
    /*Both instances have same values configured*/
    /*Make sure to reset ANC or do a mode change and come back to required mode to read the updated gain values after a Write gain is called*/
    AncReadFineGainFromInstance(AUDIO_ANC_INSTANCE_0, gain_path, &gain);

    /* We don't have a handy function for itoa and it is not commonly
       needed. sprintf is available, and is an embedded one so relatively
       efficient */
    sprintf(response, ANC_READ_FINE_GAIN_RESPONSE "%d", gain);

    DeviceTestService_CommandResponse(task, response, FULL_FINE_GAIN_RESPONSE_LEN);
    DeviceTestService_CommandResponseOk(task);
}

/*! \brief Handle the AT+ANCREADCOARSEGAIN command

    Handle the command, making sure to send either an "OK" or "ERROR" response.

    Parameters are checked, any mistakes causing an error response.

    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] ancReadCoarseGain Structure supplied by the AT command parser, providing
                               the parameters passed in the AT command (the requested 
                               mode and gain path)
*/
void DeviceTestServiceCommand_HandleAncReadCoarseGain(Task task, 
                const struct DeviceTestServiceCommand_HandleAncReadCoarseGain *ancReadCoarseGain)
{
    anc_mode_t anc_mode;
    audio_anc_path_id gain_path;
    uint16 gain=0;
    char response[FULL_COARSE_GAIN_RESPONSE_LEN];

    DEBUG_LOG_ALWAYS("DeviceTestServiceCommand_HandleAncReadCoarseGain mode:%u Path:%u ",
                     ancReadCoarseGain->mode, ancReadCoarseGain->gainpath);

    if (   !deviceTestService_AncCommandsAllowed()
         || !AncStateManager_IsEnabled()
         || !deviceTestService_AncMapMode(ancReadCoarseGain->mode, &anc_mode)
         || !deviceTestService_AncMapGainPath(ancReadCoarseGain->gainpath, &gain_path))
     {
         DeviceTestService_CommandResponseError(task);
         return;
     }
    
     /*This ANC interface makes sure to return the coarse gain for instance 0*/
     /*Both instances have same values configured*/
     /*Make sure to reset ANC or do a mode change and come back to required mode to read the updated gain values after a Write gain is called*/
     AncReadCoarseGainFromInstance(AUDIO_ANC_INSTANCE_0, gain_path, &gain);

    /* We don't have a handy function for itoa and it is not commonly
       needed. sprintf is available, and is an embedded one so relatively
       efficient */
    sprintf(response, ANC_READ_COARSE_GAIN_RESPONSE "%d", gain);

    DeviceTestService_CommandResponse(task, response, FULL_COARSE_GAIN_RESPONSE_LEN);
    DeviceTestService_CommandResponseOk(task);
}


/*! \brief Handle the AT+ANCWRITEFINEGAIN command

    Handle the command, making sure to send either an "OK" or "ERROR" response.

    Parameters are checked, any mistakes causing an error response.

    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] ancWriteFineGain Structure supplied by the AT command parser, providing
                                the parameters passed in the AT command (the requested 
                                mode, gain path and gain value)
*/
void DeviceTestServiceCommand_HandleAncWriteFineGain(Task task, 
                const struct DeviceTestServiceCommand_HandleAncWriteFineGain *ancWriteFineGain)
{
    anc_mode_t anc_mode;
    audio_anc_path_id gain_path;

    DEBUG_LOG_ALWAYS("DeviceTestServiceCommand_HandleAncWriteFindGain  mode:%u Path:%u Gain:%u ",
                     ancWriteFineGain->mode,
                     ancWriteFineGain->gainpath, ancWriteFineGain->gainvalue);

    if (   !deviceTestService_AncCommandsAllowed()
        || !AncStateManager_IsEnabled()
        || !deviceTestService_AncMapMode(ancWriteFineGain->mode, &anc_mode)
        || !deviceTestService_AncMapGainPath(ancWriteFineGain->gainpath, &gain_path)
        || ancWriteFineGain->gainvalue > 0xFF
        || !AncWriteFineGain(gain_path, ancWriteFineGain->gainvalue ))
        /*This ANC interface makes sure to write the Delta gain (difference between golden config gain and the configured gain in write)*/
    {
        DeviceTestService_CommandResponseError(task);
        return;
    }

    DeviceTestService_CommandResponseOk(task);
}


/*! \brief Handle the AT+ANCSETFINEGAINDUAL command

    Handle the command, making sure to send either an "OK" or "ERROR" response.

    Parameters are checked, any mistakes causing an error response.

    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] ancSetFineGain Structure supplied by the AT command parser, providing
                              the parameters passed in the AT command (the requested 
                              mode, gain path and gain value for instance 0 and 1)
*/
void DeviceTestServiceCommand_HandleAncSetFineGainDual(Task task, 
                    const struct DeviceTestServiceCommand_HandleAncSetFineGainDual *ancSetFineGainDual)
{
    anc_mode_t anc_mode;
    audio_anc_path_id gain_path;
    bool status_instance_0 = FALSE, status_instance_1 = FALSE;

    DEBUG_LOG_ALWAYS("DeviceTestServiceCommand_HandleAncSetFindGain  mode:%u Path:%u Instance0Gain:%u Instance1Gain:%u ",
                     ancSetFineGainDual->mode,
                     ancSetFineGainDual->gainpath, ancSetFineGainDual->instance0gain, ancSetFineGainDual->instance1gain);

    if (   !deviceTestService_AncCommandsAllowed()
        || !AncStateManager_IsEnabled()
        || !deviceTestService_AncMapMode(ancSetFineGainDual->mode, &anc_mode)
        || !deviceTestService_AncMapGainPath(ancSetFineGainDual->gainpath, &gain_path)
        || (ancSetFineGainDual->instance0gain > 0xFF)
        || ancSetFineGainDual->instance1gain > 0xFF)
    {
        DeviceTestService_CommandResponseError(task);
        return;
    }

    /*Set gain for currently set mode in DSP*/
    switch(ancSetFineGainDual->gainpath)
    {
        case FFA_PATH:
            status_instance_0 = AncConfigureFFAPathGain(AUDIO_ANC_INSTANCE_0,
                                             (uint8)ancSetFineGainDual->instance0gain);

            status_instance_1 = AncConfigureFFAPathGain(AUDIO_ANC_INSTANCE_1,
                                             (uint8)ancSetFineGainDual->instance1gain);
            break;

        case FFB_PATH:
            status_instance_0 = AncConfigureFFBPathGain(AUDIO_ANC_INSTANCE_0,
                                             (uint8)ancSetFineGainDual->instance0gain);

            status_instance_1 = AncConfigureFFBPathGain(AUDIO_ANC_INSTANCE_1,
                                             (uint8)ancSetFineGainDual->instance1gain);
            break;

        case FB_PATH:
            status_instance_0 = AncConfigureFBPathGain(AUDIO_ANC_INSTANCE_0,
                                             (uint8)ancSetFineGainDual->instance0gain);
            status_instance_1 = AncConfigureFBPathGain(AUDIO_ANC_INSTANCE_1,
                                             (uint8)ancSetFineGainDual->instance1gain);
            break;

        default:
            /* Panic here as the mapping should have ensured a valid gain_path
               A Panic would indicate a new gain_path should be supported but 
               has not been implemented here */
            Panic();
            break;
    }

    DeviceTestService_CommandResponseOkOrError(task, status_instance_0 && status_instance_1);
}


/*! \brief Handle the AT+ANCREADFINEGAINDUAL command

    Handle the command for both instances of ANC, making sure to send either an "OK" or "ERROR" response.

    Parameters are checked, any mistakes causing an error response.

    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] ancReadFineGain Structure supplied by the AT command parser, providing
                               the parameters passed in the AT command (the requested 
                               mode and gain path)
*/
void DeviceTestServiceCommand_HandleAncReadFineGainDual(Task task, 
                const struct DeviceTestServiceCommand_HandleAncReadFineGainDual *ancReadFineGainDual)
{
    anc_mode_t anc_mode;
    audio_anc_path_id gain_path;
    uint8 instance0_gain=0;
    uint8 instance1_gain=0;
    char response[FULL_FINE_GAIN_DUAL_RESPONSE_LEN];

    DEBUG_LOG_ALWAYS("DeviceTestServiceCommand_HandleAncReadFineGainDual mode:%u Path:%u ",
                     ancReadFineGainDual->mode, ancReadFineGainDual->gainpath);

    if (   !deviceTestService_AncCommandsAllowed()
        || !AncStateManager_IsEnabled()
        || !deviceTestService_AncMapMode(ancReadFineGainDual->mode, &anc_mode)
        || !deviceTestService_AncMapGainPath(ancReadFineGainDual->gainpath, &gain_path))
    {
        DeviceTestService_CommandResponseError(task);
        return;
    }

    /*This ANC interface makes sure to return the gain which golden config plus any delta from earlier calibration*/
       /*Both instances have same values configured*/
       /*Make sure to reset ANC or do a mode change and come back to required mode to read the updated gain values after a Write gain is called*/
       AncReadFineGainFromInstance(AUDIO_ANC_INSTANCE_0, gain_path, &instance0_gain);
       AncReadFineGainFromInstance(AUDIO_ANC_INSTANCE_1, gain_path, &instance1_gain);

    /* We don't have a handy function for itoa and it is not commonly
       needed. sprintf is available, and is an embedded one so relatively
       efficient */
    sprintf(response, ANC_READ_FINE_GAIN_DUAL_RESPONSE "%d %d", instance0_gain, instance1_gain);

    DeviceTestService_CommandResponse(task, response, FULL_FINE_GAIN_DUAL_RESPONSE_LEN);
    DeviceTestService_CommandResponseOk(task);
}


/*! \brief Handle the AT+ANCWRITEFINEGAINDUAL command

    Handle the command, making sure to send either an "OK" or "ERROR" response.

    Parameters are checked, any mistakes causing an error response.

    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] ancWriteFineGain Structure supplied by the AT command parser, providing
                                the parameters passed in the AT command (the requested 
                                mode, gain path and gain value for instance 0 and 1)
*/
void DeviceTestServiceCommand_HandleAncWriteFineGainDual(Task task, 
                const struct DeviceTestServiceCommand_HandleAncWriteFineGainDual *ancWriteFineGainDual)
{
    anc_mode_t anc_mode;
    audio_anc_path_id gain_path;

    DEBUG_LOG_ALWAYS("DeviceTestServiceCommand_HandleAncWriteFindGainDual  mode:%u Path:%u Instance0Gain:%u Instance1Gain:%u ",
                     ancWriteFineGainDual->mode,
                     ancWriteFineGainDual->gainpath, ancWriteFineGainDual->instance0gain, ancWriteFineGainDual->instance1gain);

    if (   !deviceTestService_AncCommandsAllowed()
        || !AncStateManager_IsEnabled()
        || !deviceTestService_AncMapMode(ancWriteFineGainDual->mode, &anc_mode)
        || !deviceTestService_AncMapGainPath(ancWriteFineGainDual->gainpath, &gain_path)
        || ancWriteFineGainDual->instance0gain > 0xFF
        || ancWriteFineGainDual->instance1gain > 0xFF
        || !AncWriteFineGainDual(gain_path, ancWriteFineGainDual->instance0gain, ancWriteFineGainDual->instance1gain))
        /*Updates gain to ANC Audio PS key after adjusting golden gains with delta gain values*/
    {
        DeviceTestService_CommandResponseError(task);
        return;
    }

    DeviceTestService_CommandResponseOk(task);
}

/*! \brief Stub command included if ANCV3 is disabled.*/
void DeviceTestServiceCommand_HandleAncV3Set(Task task,
    const struct DeviceTestServiceCommand_HandleAncV3Set* param)
{
    UNUSED(param);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANCV3 is disabled.*/
void DeviceTestServiceCommand_HandleAncV3Get(Task task,
    const struct DeviceTestServiceCommand_HandleAncV3Get* param)
{
    UNUSED(param);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANCV3 is disabled.*/
void DeviceTestServiceCommand_HandleAncV3Write(Task task,
    const struct DeviceTestServiceCommand_HandleAncV3Write* param)
{
    UNUSED(param);
    DeviceTestService_CommandResponseError(task);
}

/* end ENABLE_ANC */
#elif defined(INCLUDE_ANC_V3) && !defined(BUILD_FOR_SAL)

#include "audio_curation_objective.h"
#include "audio_curation_intent.h"
#include "audio_curation_objective_message.h"
#include "audio_curation_objective_private.h"
#include "audio_curation_router.h"
#include "audio_curation.h"
#include "phy_state.h"
#include "context_framework.h"
#include "ui_inputs.h"
#include "ui.h"
#include "message.h"
#include "logging.h"
#include "anc_audio_curation.h"
#include "audio_curation_objective_tuning.h"
#include "audio_curation_objective_transparency.h"
#include "audio_curation_monitor.h"
#include "audio_curation_hearingtest.h"
#include "audio_curation_usecase_manager.h"
#include "multidevice.h"
#include "peer_signalling.h"

#define NUMBER2INDEX(x) (x-1)
#define INDEX2NUMBER(x) (x+1)
#define SIZE_IN_BYTES(x) (x << 1)
#define SIZE_IN_32BIT(x) (x >> 1)

#define ANCV3_TUNING_MODE_BIT 0x8000
#define ANCV3_TUNING_MODE_MASK ~ANCV3_TUNING_MODE_BIT

#define ANCV3_CLEAR_CHUNK_ID (0x8000)

#define ANCV3_MAX_CHUNK_LEN_SET 24 /* Chunk length limitation through DTS interface in sizeof(UINT16) */

#define ANCV3_MAX_CHUNK_LEN_GET 24 /* Chunk length limitation through DTS interface in sizeof(UINT16) */

#define DTS_ANCV3_GET_INTERNAL_RESPONSE "+CALGET1="
#define FULL_ANCV3_GET_INTERNAL_LEN (sizeof(DTS_ANCV3_GET_INTERNAL_RESPONSE) + 4 * MAX_ANCV3_GET_CHUNK_LENGTH_CAL + 2) /* 4 chars for each Int16 */
#define DTS_ANCV3_GET_CAL_RESPONSE "+CALGET10="
#define FULL_ANCV3_GET_CAL_LEN (sizeof(DTS_ANCV3_GET_CAL_RESPONSE) + 4 * MAX_ANCV3_GET_CHUNK_LENGTH_CAL + 2) /* 4 chars for each Int16 */
#define DTS_ANCV3_GET_ANC_MODE_RESPONSE "+CALGET11="
#define FULL_ANCV3_GET_ANC_MODE_RESPONSE_LEN (sizeof(DTS_ANCV3_GET_ANC_MODE_RESPONSE) + 4 * MAX_ANCV3_GET_CHUNK_LENGTH_MODE + 2) /* 4 chars for each Int16 */
#define DTS_ANCV3_GET_TUNING_MODE_RESPONSE "+CALGET12="
#define FULL_ANCV3_GET_TUNING_MODE_RESPONSE_LEN (sizeof(DTS_ANCV3_GET_TUNING_MODE_RESPONSE) + 4 * MAX_ANCV3_GET_CHUNK_LENGTH_MODE + 2) /* 4 chars for each Int16 */

#define DTS_ANCV3_CAL_VERSION "1.2"

typedef enum
{
    ANCV3_DIR_SET = 0,
    ANCV3_DIR_GET = 1,
} ancv3_direction_t;

typedef enum
{
    ANCV3_COMMAND_ID_UNDEFINED = 0,
    ANCV3_COMMAND_ID_INTERNAL = 1,

    ANCV3_COMMAND_ID_CAL = 10,
    ANCV3_COMMAND_ID_ANC_MODE = 11,
    ANCV3_COMMAND_ID_TUNING_MODE = 12,
} ancv3_command_ids_t;

/* Maximum chunk length in sizeof(UINT16) */
typedef enum
{
    MAX_ANCV3_GET_CHUNK_LENGTH_MODE = 1,
    MAX_ANCV3_GET_CHUNK_LENGTH_CAL = ANCV3_MAX_CHUNK_LEN_GET,
} ancv3_max_chunk_length_t;

/* Length definitions in sizeof(UINT16) */
typedef enum
{
    ANCV3_PAYLOAD_CAL_HEADER_INDEX_SUBGRAPH_ID = 0,
    ANCV3_PAYLOAD_CAL_HEADER_INDEX_MODULE_ID = 1,
    ANCV3_PAYLOAD_CAL_HEADER_INDEX_INSTANCE_ID = 2,
    ANCV3_PAYLOAD_CAL_HEADER_INDEX_CKV_ID = 3,
    ANCV3_PAYLOAD_CAL_HEADER_INDEX_CKV_VALUE = 4,
    ANCV3_PAYLOAD_CAL_HEADER_INDEX_PID = 5,
    ANCV3_PAYLOAD_CAL_HEADER_INDEX_TOTAL_LEN = 6,   /* For Set and Write: following payload length, for Get: receive payload length */
    ANCV3_PAYLOAD_CAL_HEADER_LEN = 14,
} ancv3_header_length_and_index_t;

typedef struct
{
    uint16 mode;
    uint16 empty;
} ancv3_payload_mode_t;

typedef union
{
    uint8  bytes[ANCV3_MAX_CHUNK_LEN_SET<<1];
    uint16 words_16bit[ANCV3_MAX_CHUNK_LEN_SET];
    uint32 words_32bit[ANCV3_MAX_CHUNK_LEN_SET>>1];
    ancv3_payload_mode_t         anc_mode;
} ancv3_set_payload_buffer_t;

typedef struct
{
    uint32 subgraph_id;         /* QACT outer box ID */
    uint32 module_id;           /* QACT Module ID in properties window */
    uint32 cap_id;              /* 16 LSBs of module_id */
    uint32 instance_id;         /* QACT Instance ID in properties window */
    uint32 ckv_id;              /* CKV ID */
    uint32 ckv_value;           /* CKV Value */
    uint32 pid;                 /* Parameter ID */
    uint32 total_payload_size;  /* in sizeof(UINT16) */
    uint32 ats_header_size;     /* size of ATS send header in sizeof(UINT16) */
} ancv3_payload_header_t;

typedef struct
{
    ancv3_command_ids_t command_id;
    uint16 chunk_id;            /* Chunk ID */
    uint8* total_payload_pt;    /* Payload is collected in chunks */
} ancv3_set_command_t;

static struct 
{
    uint32 is_tuning_mode : 1;
    uint32 is_enabled : 1;
    ancv3_payload_header_t header;
    ancv3_set_command_t set;
} ancv3_state;

typedef union
{
    uint8  bytes[SIZE_IN_BYTES(ANCV3_MAX_CHUNK_LEN_GET)];
    uint16 words_16bit[ANCV3_MAX_CHUNK_LEN_GET];
    uint32 words_32bit[SIZE_IN_32BIT(ANCV3_MAX_CHUNK_LEN_GET)];
} ancv3_get_payload_buffer_t;

static bool deviceTestService_AncCommandsAllowed(void)
{
    return DeviceTestService_CommandsAllowed();
}

static bool deviceTestServiceCommand_IsSetCommandInProgress(void)
{
    return ancv3_state.set.command_id != ANCV3_COMMAND_ID_UNDEFINED;
}

static bool deviceTestServiceCommand_IsFirstChunk(uint16 nr_of_chunks, uint16 chunk_id)
{
    return (NUMBER2INDEX(nr_of_chunks) == chunk_id);
}

static bool deviceTestServiceCommand_IsLastChunk(uint16 nr_of_chunks, uint16 chunk_id)
{
    UNUSED(nr_of_chunks);
    return (chunk_id == 0);
}

/*! \brief Check if tuning mode is selected.
           If tuning mode is selected, return the anc_mode ID without the tuning bit
    \param mode combination of mode ID and tuning mode bit
    \param anc_mode returns ANC mode ID without tuning mode bit
    \return FALSE = no tuning mode, TRUE = tuning mode
*/
static bool deviceTestServiceCommand_IsTuningMode(uint16 mode, uint8 *anc_mode)
{
    *anc_mode = mode;
    ancv3_state.is_tuning_mode = FALSE;
    if (mode & ANCV3_TUNING_MODE_BIT)
    {
        *anc_mode = mode & ANCV3_TUNING_MODE_MASK;
        ancv3_state.is_tuning_mode = TRUE;
        return TRUE;
    }
    return FALSE;
}

/*! \brief Convert the payload from ASCII to binary
    \param payload_data_ptr pointer to the characters of the payload to be converted
    \param payload_len length of payload to be converted
    \param max_len maximum allowed payload length for range check
    \param *converted Converted binary numbers in UINT16 format
    \param *converted_len length of binary data in sizeof(UINT16)
    \return FALSE = no error, TRUE = error
*/
static bool deviceTestServiceCommand_ConvertAsciiToBinary(uint8* payload_data_ptr, uint16 payload_len, uint16 max_len, uint16* converted, uint16* converted_len)
{
    unsigned nibbles = 0;
    uint16 value = 0;
    uint16 len = 0;
    uint16* next_word = converted;
    bool error = FALSE;

    while (payload_len--)
    {
        char ch = *payload_data_ptr++;
        if (!isxdigit(ch))
        {
            if (ch == ' ')
            {
                if (nibbles == 0)
                {
                    continue;
                }
            }
            error = TRUE;
            break;
        }
        value = (value << 4) + deviceTestService_HexToNumber(ch);
        if (++nibbles == 4)
        {
            *next_word++ = value;
            value = 0;
            nibbles = 0;
            len++;
        }
    }

    if (error || nibbles != 0)
    {
        DTS_LOG_ERROR("deviceTestServiceCommand_ConvertAsciiToBinary: Conversion failed error %d nibbles %d. 4-nibble numbers expected", error, nibbles);
        return TRUE;
    }
    *converted_len = len;
    return FALSE;
}

/*! \brief Convert the binary payload to ASCII
    \param *payload pointer to the binary UINT16 numbers
    \param payload_len length of payload to be converted
    \param *ascii_ptr pointer to the ASCII characters
*/
static void deviceTestServiceCommand_ConvertBinaryToAscii(uint16* payload, uint16 payload_len, char* ascii_ptr)
{
    while (payload_len--)
    {
        uint16 word = *payload++;
        *ascii_ptr++ = deviceTestService_NumberToHex((word & 0xF000) >> 12);
        *ascii_ptr++ = deviceTestService_NumberToHex((word & 0x0F00) >> 8);
        *ascii_ptr++ = deviceTestService_NumberToHex((word & 0x00F0) >> 4);
        *ascii_ptr++ = deviceTestService_NumberToHex((word & 0x000F) >> 0);
    }
}

/*! \brief Calculates the number of chunks to be transferred
    \param total_payload_size length of the whole payload
    \param max_chunk_size length of one chunk
    \param header_len in sizeof(UINT16)
    \param *nr_of_chunks number of chunks to be transferred
    \param *rest number of values in sizeof(UINT16)
*/
static void deviceTestServiceCommand_CalculateNumberOfChunks(uint16 total_payload_size, 
                uint16 max_chunk_size, uint16 header_len, uint16* nr_of_chunks, uint16* rest)
{
    uint16 nr_chunks = 0;
    uint16 size_of_rest = 0;

    if (max_chunk_size == 0)
    {
        DTS_LOG_ERROR("deviceTestServiceCommand_CalculateNumberOfChunks: max_chunk_size is 0!");
    }
    else
    {
        nr_chunks = (total_payload_size + header_len) / max_chunk_size;
        size_of_rest = (total_payload_size + header_len) % max_chunk_size;
        if (size_of_rest > 0)
        {
            nr_chunks++;
        }
        DTS_LOG_VERBOSE("deviceTestServiceCommand_CalculateNumberOfChunks for payload %d and header %d: nr_of_chunks %d rest %d", 
                        total_payload_size, header_len, nr_chunks, size_of_rest);
    }
    *nr_of_chunks = nr_chunks;
    *rest = size_of_rest;
}

/*! \brief Decrement chunk ID or set chunk ID when state.chunk_id is 0.
           This is only needed for Set/Write commands. 
           With the Get command all chunks are transferred within a for loop. 
           No internal chunk ID is needed
    \param max_chunk_id to clear or set internal chunk ID counter
    \return current chunk ID
*/
static uint16 deviceTestServiceCommand_SetOrDecrementChunkId(uint16 max_chunk_id)
{
    if ((max_chunk_id & ANCV3_CLEAR_CHUNK_ID) != 0)
    {
        ancv3_state.set.chunk_id = 0;
        DTS_LOG("deviceTestServiceCommand_SetOrDecrementChunkId: Reseting chunk_id to %d",
            ancv3_state.set.chunk_id);
    }
    else if (ancv3_state.set.chunk_id == 0)
    {
        ancv3_state.set.chunk_id = max_chunk_id;
        DTS_LOG_VERBOSE("deviceTestServiceCommand_SetOrDecrementChunkId: Setting chunk_id to %d",
            ancv3_state.set.chunk_id);
    }
    else
    {
        if (ancv3_state.set.chunk_id > 0)
        {
            ancv3_state.set.chunk_id--;
        }
        DTS_LOG_VERBOSE("deviceTestServiceCommand_SetOrDecrementChunkId: Decrementing chunk_id to %d",
                        ancv3_state.set.chunk_id);
    }
    return ancv3_state.set.chunk_id;
}

/*! \brief Increment chunk ID in case it has already been decremented 
           and payload length is invalid.
    \param max_chunk_id
*/
static void deviceTestServiceCommand_RevertDecrementChunkId(uint16 max_chunk_id)
{
    ancv3_state.set.chunk_id++;
    if (ancv3_state.set.chunk_id > max_chunk_id)
    {
        ancv3_state.set.chunk_id = 0;
    }
    DTS_LOG_VERBOSE("deviceTestServiceCommand_RevertDecrementChunkId: Reverting chunk_id to %d",
                    ancv3_state.set.chunk_id);
}


/*! \brief Swap UINT32
    \param value: UINT32 to swap
    \return swapped value
*/
static uint32 deviceTestServiceCommand_SwapUint32(uint32 value)
{
    return (value >> 16) | ((value & 0xFFFF) << 16);
}

/*! \brief Swap a complete payload
    \param payload pointer to payload
    \param header_len length of header in payload [32bit]
    \param payload_len length of payload [32bit]
*/
static void deviceTestServiceCommand_SwapPayload(uint32* payload, uint16 header_len, uint16 payload_len)
{
    uint32 swapped = 0;

    for (uint16 jj = 0; jj < payload_len; jj++)
    {
        swapped = deviceTestServiceCommand_SwapUint32(payload[jj + header_len]);
        DTS_LOG_VERBOSE("deviceTestServiceCommand_SwapPayload[%3d]: 0x%8x -> 0x%8x",
                        jj + header_len, payload[jj + header_len], swapped);
        payload[jj + header_len] = swapped;
    }
}

/*! \brief Get payload size and pid from payload
           Allocate memory for the complete payload. 
           Save values internally that are only received with the first chunk of data.
    \param command_id command ID. When a transfer is started with a specific 
           command ID the system expects the full payload before the next command ID
    \param total_payload_size payload size to allocate [16bit]
    \param pid parameter ID is stored internally since only available in first chunk
*/
static void deviceTestServiceCommand_AllocateMemory(ancv3_command_ids_t command_id, uint32 total_payload_size)
{
    ancv3_state.set.command_id = command_id;
    DTS_LOG("deviceTestServiceCommand_AllocateMemory: total_payload_size %d", total_payload_size);
    ancv3_state.set.total_payload_pt = malloc(SIZE_IN_BYTES(total_payload_size));
    if (!ancv3_state.set.total_payload_pt)
    {
        DTS_LOG_PANIC("deviceTestServiceCommand_AllocateMemory: Memory allocation failed");
    }
    memset(ancv3_state.set.total_payload_pt, 0, SIZE_IN_BYTES(total_payload_size));
}

/*! \brief Free the allocated memory
*/
static void deviceTestServiceCommand_FreeMemory(void)
{
    ancv3_state.header.total_payload_size = 0;
    ancv3_state.header.ats_header_size = 0;
    ancv3_state.header.pid = 0;
    ancv3_state.set.command_id = ANCV3_COMMAND_ID_UNDEFINED;
    if (ancv3_state.set.total_payload_pt)
    {
        free(ancv3_state.set.total_payload_pt);
        DTS_LOG_VERBOSE("deviceTestServiceCommand_FreeMemory");
    }
}

/*! \brief Calculate payload offset in bytes
           Chunk_id starts with the highest number, counting back to 0
    \param nr_of_chunks
    \param chunk_id
    \param header_len in Bytes
    \param max_chunk_size in Bytes
*/
static uint16 deviceTestServiceCommand_CalcTotalPayloadOffset(uint16 nr_of_chunks, uint16 chunk_id, uint16 header_len, uint16 max_chunk_size)
{
    uint16 payload_offset = (NUMBER2INDEX(nr_of_chunks) - chunk_id) * max_chunk_size;
    if (payload_offset > 0)
    {
        payload_offset -= header_len;
    }
    DTS_LOG("deviceTestServiceCommand_CalcTotalPayloadOffset: header_len %d [Bytes] payload_offset %d [bytes]",
            header_len, payload_offset);
    return payload_offset;
}

/*! \brief Calculates the payload length for the next transfer
    \param nr_of_chunks number of chunks to transfer
    \param chunk_id ID of the next chunk
    \param max_chunk_size the maximum size of a chunk
    \param header_len in sizeof(UINT16)
    \param rest the rest of the last chunk
    \return payload length
*/
static uint16 deviceTestServiceCommand_CalculateExpectedPayloadLen(uint16 nr_of_chunks, uint16 chunk_id, 
                  uint16 max_chunk_size, uint16 header_len, uint16 rest)
{
    uint16 payload_len = 0;

    if (!deviceTestServiceCommand_IsLastChunk(nr_of_chunks, chunk_id) || rest == 0)
    {
        payload_len = max_chunk_size;
    }
    else
    {
        payload_len = rest;
    }
    if (deviceTestServiceCommand_IsFirstChunk(nr_of_chunks, chunk_id))
    {
        DTS_LOG("deviceTestServiceCommand_CalculateExpectedPayloadLen: %d - header %d for first chunk", payload_len, header_len);
        payload_len -= header_len;
    }

    DTS_LOG("deviceTestServiceCommand_CalculateExpectedPayloadLen: %d", payload_len);
    return payload_len;
}

/*! \brief Check payload length
    \param nr_of_chunks
    \param chunk_id
    \param header_len in sizeof(UINT16)
    \param converted_len in sizeof(UINT16)
    \param expected_payload_len in sizeof(UINT16)
*/
static bool deviceTestServiceCommand_CheckCorrectPayloadLen(
                uint16 nr_of_chunks, uint16 chunk_id, uint16 header_len, uint16 converted_len, uint16 expected_payload_len)
{
    DTS_LOG_VERBOSE("deviceTestServiceCommand_CheckCorrectPayloadLen: nr_of_chunks %d chunk_id %d conv_len %d expected_len %d",
                    nr_of_chunks, chunk_id, converted_len, expected_payload_len);

    /* The last chunk needs to compensate for the header inside the payload */
    if (deviceTestServiceCommand_IsFirstChunk(nr_of_chunks, chunk_id))
    {
        DTS_LOG_VERBOSE("deviceTestServiceCommand_CheckCorrectPayloadLen: - header %d", header_len);
        converted_len -= header_len;
    }
    if (converted_len != expected_payload_len)
    {
        DTS_LOG_ERROR("deviceTestServiceCommand_CheckCorrectPayloadLen: Invalid payload length %d, expected %d",
                      converted_len, expected_payload_len);
        return FALSE;
    }

    if (expected_payload_len % 2)
    {
        DTS_LOG_ERROR("deviceTestServiceCommand_CheckCorrectPayloadLen: Expecting 32bit alignment, received %d",
                      expected_payload_len);
        return FALSE;
    }
    return TRUE;
}

/*! \brief Extract the IDs from the command payload
    \param payload the payload including the IDs
    \param header Struct that holds the IDs
    \param dir: Direction SET / GET. The GET does not have additional payload, the total payload length is not extracted.
*/
static void deviceTestServiceCommand_ExtractIDs(uint32* payload, ancv3_payload_header_t* header)
{
    header->subgraph_id = deviceTestServiceCommand_SwapUint32(payload[ANCV3_PAYLOAD_CAL_HEADER_INDEX_SUBGRAPH_ID]);
    header->module_id = deviceTestServiceCommand_SwapUint32(payload[ANCV3_PAYLOAD_CAL_HEADER_INDEX_MODULE_ID]);
    header->cap_id = header->module_id & 0xFFFF;
    header->instance_id = deviceTestServiceCommand_SwapUint32(payload[ANCV3_PAYLOAD_CAL_HEADER_INDEX_INSTANCE_ID]);
    header->ckv_id = deviceTestServiceCommand_SwapUint32(payload[ANCV3_PAYLOAD_CAL_HEADER_INDEX_CKV_ID]);
    header->ckv_value = deviceTestServiceCommand_SwapUint32(payload[ANCV3_PAYLOAD_CAL_HEADER_INDEX_CKV_VALUE]);
    header->pid = deviceTestServiceCommand_SwapUint32(payload[ANCV3_PAYLOAD_CAL_HEADER_INDEX_PID]);
    header->total_payload_size = deviceTestServiceCommand_SwapUint32(payload[ANCV3_PAYLOAD_CAL_HEADER_INDEX_TOTAL_LEN]);
    DTS_LOG("deviceTestServiceCommand_ExtractIDs: SubGraph 0x%x Module 0x%x Inst 0x%x CKV 0x%x 0x%x PID 0x%x size %d",
        header->subgraph_id, header->module_id, header->instance_id, header->ckv_id, header->ckv_value, header->pid, header->total_payload_size);
}

/*! \brief Fills receive buffer of OOB message with additional data from the payload of the CALGET command
    \param payload the incoming payload
    \param command_payload_len complete payload length in sizeof(uint16)
    \param rx_buffer the receive buffer that needs to be pre-filled
*/
static void deviceTestServiceCommand_PrefillReceiveBuffer(uint32* payload, uint16 payload_len, uint32* rx_buffer)
{
    uint16 rx_ix = 0;
    uint16 prefill_data_len = NUMBER2INDEX(SIZE_IN_32BIT(payload_len)) - ANCV3_PAYLOAD_CAL_HEADER_INDEX_TOTAL_LEN;
    for (uint16 payload_ix = 0; payload_ix < prefill_data_len; payload_ix++)
    {
        rx_buffer[rx_ix] = deviceTestServiceCommand_SwapUint32(payload[INDEX2NUMBER(ANCV3_PAYLOAD_CAL_HEADER_INDEX_TOTAL_LEN) + payload_ix]);
        DTS_LOG("deviceTestServiceCommand_PrefillReceiveBuffer[%d] = 0x%x", rx_ix, rx_buffer[rx_ix]);
        rx_ix++;
    }
}

/*! \brief Fill the ATS send header
    \param header Struct that holds the IDs
    \param total_payload_pt the payload that is sent to ATS layer
    \return ATS header length [sizeof(UINT16)}
*/
#define MAX_ATS_HEADER_LEN (10 * 2)    //sizeof(UINT16)
static uint16 deviceTestServiceCommand_FillAtsHeader(ancv3_payload_header_t header, uint8* total_payload_pt)
{
    uint32* buffer_pt = total_payload_pt;
    uint32 ix = 1;
    // buffer_pt[0] will be filled at the end                               // [0] size [bytes]
    buffer_pt[ix++] = 1;                                                    // [1] num subgraphs
    buffer_pt[ix++] = header.subgraph_id;                                   // [2] subgraph ID
    if (header.ckv_id == 0)
    {
        buffer_pt[ix++] = 0;                                                // [3] num CKVs
    }
    else
    {
        buffer_pt[ix++] = 1;                                                // [3] num CKVs
        buffer_pt[ix++] = header.ckv_id;                                    // [4] CKV Key
        buffer_pt[ix++] = header.ckv_value;                                 // [5] CKV Value
    }
    buffer_pt[ix++] = SIZE_IN_BYTES(header.total_payload_size) + (3 * 4);   // [6] size [bytes]
    /* according to _acdb_subgraph_param_data_t */
    /* <Instance ID, Param ID, Size, Error Code, Data[Size]> */
    buffer_pt[ix++] = header.instance_id;                                   // [7] instance ID
    buffer_pt[ix++] = header.pid;                                           // [8] Param ID
    buffer_pt[ix++] = SIZE_IN_BYTES(header.total_payload_size) + (0 * 4);   // [9] size [bytes]
    
    buffer_pt[0] = SIZE_IN_BYTES(header.total_payload_size) + ((ix - 1) * 4);  // size [bytes]
    
    return ix << 1;
}

/*! \brief Common code for writing to delta ACDB or to set parameters directly with the module
    \param command_id Command ID
    \param payload the ASCII converted payload
    \param converted_length the length of the ASCII converted payload
    \param persistent FALSE: Set parameters to module TRUE: Write to delta ACDB
*/
static bool deviceTestServiceCommand_SetCalibration(uint32 command_id, ancv3_set_payload_buffer_t payload, uint32 converted_len, bool persistent)
{
    uint16 nr_of_chunks = 0;
    uint16 rest = 0;

    /* First chunk received? */
    if (!deviceTestServiceCommand_IsSetCommandInProgress())
    {
        /* Extract IDs from command payload. Allocate memory for the whole payload */
        deviceTestServiceCommand_ExtractIDs(&payload.words_32bit, &ancv3_state.header);
        deviceTestServiceCommand_AllocateMemory(command_id, ancv3_state.header.total_payload_size + MAX_ATS_HEADER_LEN);
        /* Fill the ATS send header */
        ancv3_state.header.ats_header_size = deviceTestServiceCommand_FillAtsHeader(ancv3_state.header, ancv3_state.set.total_payload_pt);
    }

    /* Update chunk_id and calculate expected payload length */
    deviceTestServiceCommand_CalculateNumberOfChunks(ancv3_state.header.total_payload_size,
        ANCV3_MAX_CHUNK_LEN_SET, ANCV3_PAYLOAD_CAL_HEADER_LEN, &nr_of_chunks, &rest);
    uint16 chunk_id = deviceTestServiceCommand_SetOrDecrementChunkId(NUMBER2INDEX(nr_of_chunks));
    uint16 expected_payload_len = deviceTestServiceCommand_CalculateExpectedPayloadLen(nr_of_chunks, chunk_id,
        ANCV3_MAX_CHUNK_LEN_SET, ANCV3_PAYLOAD_CAL_HEADER_LEN, rest);

    if (!deviceTestServiceCommand_CheckCorrectPayloadLen(nr_of_chunks, chunk_id,
        ANCV3_PAYLOAD_CAL_HEADER_LEN, converted_len, expected_payload_len))
    {
        deviceTestServiceCommand_RevertDecrementChunkId(NUMBER2INDEX(nr_of_chunks));
        return TRUE;
    }

    /* Swap the payload */
    uint16 header_len = ANCV3_PAYLOAD_CAL_HEADER_LEN;
    if (!deviceTestServiceCommand_IsFirstChunk(nr_of_chunks, chunk_id))
    {
        /* The header only needs to be considered for the first chunk */
        header_len = 0;
    }
    deviceTestServiceCommand_SwapPayload(payload.words_32bit, SIZE_IN_32BIT(header_len), SIZE_IN_32BIT(expected_payload_len));

    DTS_LOG("deviceTestServiceCommand_SetCalibration: chunk_id %d", chunk_id);

    /* Copy chunk to total payload */
    uint16 total_payload_offset_bytes = deviceTestServiceCommand_CalcTotalPayloadOffset(
        nr_of_chunks, chunk_id, SIZE_IN_BYTES(ANCV3_PAYLOAD_CAL_HEADER_LEN), SIZE_IN_BYTES(ANCV3_MAX_CHUNK_LEN_SET));
    memcpy(ancv3_state.set.total_payload_pt + total_payload_offset_bytes + SIZE_IN_BYTES(ancv3_state.header.ats_header_size),
        payload.bytes + SIZE_IN_BYTES(header_len), SIZE_IN_BYTES(expected_payload_len));

    /* Write complete payload to the system */
    if (deviceTestServiceCommand_IsLastChunk(nr_of_chunks, chunk_id))
    {
        ancv3_state.header.total_payload_size += ancv3_state.header.ats_header_size;

        uint32* buffer_pt = ancv3_state.set.total_payload_pt;
        for (uint16 jj = 0; jj < SIZE_IN_32BIT(ancv3_state.header.total_payload_size); jj++)
        {
            DTS_LOG_VERBOSE("deviceTestServiceCommand_SetCalibration: [%3d] 0x%8x", jj, buffer_pt[jj]);
        }

        if (!AudioCurationRouter_SetCalibration(SIZE_IN_BYTES(ancv3_state.header.total_payload_size), ancv3_state.set.total_payload_pt, persistent))
        {
            return TRUE;
        }
        deviceTestServiceCommand_FreeMemory();
    }
    return FALSE;
}

/*! \brief Handle the AT+ANCDISABLE command for ANC_V3
    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
*/
void DeviceTestServiceCommand_HandleAncDisable(Task task)
{
    UNUSED(task);

    if (!deviceTestService_AncCommandsAllowed())
    {
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncDisable: Invalid state %d",
                       deviceTestService_AncCommandsAllowed());
        DeviceTestService_CommandResponseError(task);
        return;
    }

    if (ancv3_state.is_tuning_mode)
    {
        DTS_LOG("DeviceTestServiceCommand_HandleAncDisable: Tuning mode");
        appTestAudioCuration_ExitTuning();
    }
    else
    {
        DTS_LOG("DeviceTestServiceCommand_HandleAncDisable: ANC mode");
        appTestAudioCuration_SetAncDisable();
    }
    ancv3_state.is_enabled = FALSE;
    ancv3_state.is_tuning_mode = FALSE;
    deviceTestServiceCommand_SetOrDecrementChunkId(ANCV3_CLEAR_CHUNK_ID);
}

/*! \brief Handle the AT+ANCENABLE command for ANC_V3
    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] ancEnable Structure supplied by the AT command parser, providing
               the parameters passed in the AT command (the requested mode to enable)
*/
void DeviceTestServiceCommand_HandleAncEnable(Task task,
    const struct DeviceTestServiceCommand_HandleAncEnable* command)
{
    uint8 anc_mode = command->mode;
    UNUSED(task);

    if (!deviceTestService_AncCommandsAllowed())
    {
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncEnable: Invalid state %d",
                      deviceTestService_AncCommandsAllowed());
        DeviceTestService_CommandResponseError(task);
        return;
    }

    if (!ancv3_state.is_enabled)
    {
        DTS_LOG("DeviceTestServiceCommand_HandleAncEnable: ANC mode");
        appTestAudioCuration_SetAncEnable();
    }
    DTS_LOG("DeviceTestServiceCommand_HandleAncEnable: SetMode %d", anc_mode);
    appTestAudioCuration_SetMode(anc_mode);

    if (anc_mode > 0)
    {
        ancv3_state.is_enabled = TRUE;
    }
    else
    {
        ancv3_state.is_enabled = FALSE;
    }
    deviceTestServiceCommand_SetOrDecrementChunkId(ANCV3_CLEAR_CHUNK_ID);
}

/*! \brief Handle the AT+CALSET command
    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] command Structure supplied by the AT command parser, providing
               the parameters passed in the AT command
*/
void DeviceTestServiceCommand_HandleAncV3Set(Task task,
    const struct DeviceTestServiceCommand_HandleAncV3Set* command)
{
    UNUSED(task);
    ancv3_set_payload_buffer_t payload; /* buffer for binary converted payload data */
    uint16 converted_len = 0;     /* in sizeof(UINT16) */
    const uint8* payload_data_ptr = command->payload.data;
    bool error = FALSE;

    if (!deviceTestService_AncCommandsAllowed() && ancv3_state.is_enabled)
    {
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Set: Invalid state %d %d",
                      deviceTestService_AncCommandsAllowed(), ancv3_state.is_enabled);
        DeviceTestService_CommandResponseError(task);
        return;
    }

    if (command == NULL)
    {
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Set: command pointer is invalid");
        DeviceTestService_CommandResponseError(task);
        return;
    }

    /* Set command: Convert ASCII payload to binary */
    memset(payload.words_16bit, 0, SIZE_IN_BYTES(ANCV3_MAX_CHUNK_LEN_SET));
    if (deviceTestServiceCommand_ConvertAsciiToBinary(payload_data_ptr,
            command->payload.length, ANCV3_MAX_CHUNK_LEN_SET, payload.words_16bit, &converted_len))
    {
        DeviceTestService_CommandResponseError(task);
        return;
    }

    switch (command->id)
    {
    /**** Set Tuning Mode */
    case ANCV3_COMMAND_ID_TUNING_MODE:
        payload.anc_mode.mode |= ANCV3_TUNING_MODE_BIT;

    /**** Set ANC Mode */
    case ANCV3_COMMAND_ID_ANC_MODE:
    {
        uint8 anc_mode = 0;
        if (deviceTestServiceCommand_IsTuningMode(payload.anc_mode.mode, &anc_mode))
        {
            /* Tuning mode requested */
            if (ancv3_state.is_tuning_mode)
            {
                DTS_LOG("DeviceTestServiceCommand_HandleAncV3Set: Tuning Mode %d", anc_mode);
                if (anc_mode > 0)
                {
                    appTestAudioCuration_EnableTuning(TRUE);
                    appTestAudioCuration_EnterTuning(anc_mode);
                }
                else
                {
                    appTestAudioCuration_ExitTuning();
                    appTestAudioCuration_EnableTuning(FALSE);
                }
            }
            else
            {
                DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Set: Switching from ANC mode to Tuning mode not supported");
            }
        }
        else
        {
            /* ANC mode requested */
            if (ancv3_state.is_tuning_mode)
            {
                DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Set: Switching from Tuning mode to ANC mode not supported");
            }
            else
            {
                DTS_LOG("DeviceTestServiceCommand_HandleAncV3Set: ANC Mode %d", anc_mode);
                appTestAudioCuration_SetMode(anc_mode);
            }
        }
    }
    break;
    
    /**** CAL SET */
    case ANCV3_COMMAND_ID_CAL:
    {
#if 0
        /* CAL Set: Common code to set module directly */
        error = deviceTestServiceCommand_SetCalibration(command->id, payload, converted_len, FALSE);
#else
        uint16 nr_of_chunks = 0;
        uint16 rest = 0;
        
        if (!deviceTestServiceCommand_IsSetCommandInProgress())
        {
            /* First chunk received. Extract header. Allocate memory for the whole payload */
            deviceTestServiceCommand_ExtractIDs(&payload.words_32bit, &ancv3_state.header);
            deviceTestServiceCommand_AllocateMemory(command->id, ancv3_state.header.total_payload_size);
        }

        /* Update chunk_id and calculate expected payload length */
        deviceTestServiceCommand_CalculateNumberOfChunks(ancv3_state.header.total_payload_size, 
            ANCV3_MAX_CHUNK_LEN_SET, ANCV3_PAYLOAD_CAL_HEADER_LEN, &nr_of_chunks, &rest);
        uint16 chunk_id = deviceTestServiceCommand_SetOrDecrementChunkId(NUMBER2INDEX(nr_of_chunks));
        uint16 expected_payload_len = deviceTestServiceCommand_CalculateExpectedPayloadLen(nr_of_chunks, chunk_id,
                                          ANCV3_MAX_CHUNK_LEN_SET, ANCV3_PAYLOAD_CAL_HEADER_LEN, rest);

        if (!deviceTestServiceCommand_CheckCorrectPayloadLen(nr_of_chunks, chunk_id, 
                   ANCV3_PAYLOAD_CAL_HEADER_LEN, converted_len, expected_payload_len))
        {
            deviceTestServiceCommand_RevertDecrementChunkId(NUMBER2INDEX(nr_of_chunks));
            error = TRUE;
            break;
        }

        /* Swap the payload */
        uint16 header_len = ANCV3_PAYLOAD_CAL_HEADER_LEN;
        if (!deviceTestServiceCommand_IsFirstChunk(nr_of_chunks, chunk_id))
        {
            /* The header only needs to be considered for the first chunk */
            header_len = 0;
        }
        deviceTestServiceCommand_SwapPayload(payload.words_32bit, SIZE_IN_32BIT(header_len), SIZE_IN_32BIT(expected_payload_len));

        DTS_LOG("DeviceTestServiceCommand_HandleAncV3Set: Calibration chunk_id %d", chunk_id);

        /* Copy chunk to total payload */
        uint16 total_payload_offset_bytes = deviceTestServiceCommand_CalcTotalPayloadOffset(
                   nr_of_chunks, chunk_id, SIZE_IN_BYTES(ANCV3_PAYLOAD_CAL_HEADER_LEN), SIZE_IN_BYTES(ANCV3_MAX_CHUNK_LEN_SET));
        memcpy(ancv3_state.set.total_payload_pt + total_payload_offset_bytes,
               payload.bytes + SIZE_IN_BYTES(header_len), SIZE_IN_BYTES(expected_payload_len));

        uint32* buffer_pt = ancv3_state.set.total_payload_pt;
        for (uint16 jj = 0; jj < SIZE_IN_32BIT(ancv3_state.header.total_payload_size); jj++)
        {
            DTS_LOG_VERBOSE("DeviceTestServiceCommand_HandleAncV3Set: Calibration [%3d] 0x%8x", jj, buffer_pt[jj]);
        }

        /* Write complete payload to capability */
        if (deviceTestServiceCommand_IsLastChunk(nr_of_chunks, chunk_id))
        {
            if (!appTestAudioCuration_WriteCapParam(ancv3_state.header.cap_id, 0, ancv3_state.header.pid,
                     SIZE_IN_BYTES(ancv3_state.header.total_payload_size), ancv3_state.set.total_payload_pt))
            {
                DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Set: Calibration: Writing cap_id 0x%x failed",
                              ancv3_state.header.cap_id);
                error = TRUE;
            }
            deviceTestServiceCommand_FreeMemory();
        }
#endif
    }
    break;
    
    /* Reset internal states */
    case ANCV3_COMMAND_ID_INTERNAL:
        DEBUG_LOG_ALWAYS("ANCV3_COMMAND_ID_INTERNAL: Payload 0x%x", payload.words_16bit[0]);
        switch (payload.words_16bit[0])
        {
            case 0:
                deviceTestServiceCommand_SetOrDecrementChunkId(ANCV3_CLEAR_CHUNK_ID);
                ancv3_state.is_tuning_mode = FALSE;
                ancv3_state.is_enabled = FALSE;
                deviceTestServiceCommand_FreeMemory();
                break;

            case 1:
                DTS_LOG("DeviceTestServiceCommand_HandleAncV3Set: Deactivating DSP deep sleep...");
                OperatorsFrameworkSetDeepSleep(FALSE);
                break;

            case 2:
                DTS_LOG("DeviceTestServiceCommand_HandleAncV3Set: Activating DSP deep sleep...");
                OperatorsFrameworkSetDeepSleep(TRUE);
                break;

            case 3:
                DTS_LOG("DeviceTestServiceCommand_HandleAncV3Set: Factory reset...");
                Ui_InjectUiInput(ui_input_force_reset);
                break;

            case 4:
                DTS_LOG("DeviceTestServiceCommand_HandleAncV3Set: Clear pairing list...");
                Ui_InjectUiInput(ui_input_sm_delete_handsets);
                break;

            default:
                DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Set: Unhandled payload %u", payload.words_16bit[0]);
                error = TRUE;
                break;
        }


    break;

    default:
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Set: Invalid command_id %u", command->id);
        error = TRUE;
        break;
    }
    
    if (error)
    {
        DeviceTestService_CommandResponseError(task);
    }
}

/*! \brief Handle the AT+CALWRITE
    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] command Structure supplied by the AT command parser, providing
               the parameters passed in the AT command
*/
void DeviceTestServiceCommand_HandleAncV3Write(Task task,
    const struct DeviceTestServiceCommand_HandleAncV3Write* command)
{
    UNUSED(task);
    ancv3_set_payload_buffer_t payload; /* buffer for binary converted payload data */
    uint16 converted_len = 0;           /* in sizeof(UINT16) */
    const uint8* payload_data_ptr = command->payload.data;
    bool error = FALSE;

    if (!deviceTestService_AncCommandsAllowed())
    {
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Write: Invalid state %d", deviceTestService_AncCommandsAllowed());
        DeviceTestService_CommandResponseError(task);
        return;
    }

    if (command == NULL)
    {
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Write: command pointer is invalid");
        DeviceTestService_CommandResponseError(task);
        return;
    }

    switch (command->id)
    {
    /**** CAL Write */
    case ANCV3_COMMAND_ID_CAL:
    {
        /* Convert Hex payload to bin */
        memset(&payload, 0, sizeof(payload));
        if (deviceTestServiceCommand_ConvertAsciiToBinary(payload_data_ptr,
                command->payload.length, ancv3_state.header.total_payload_size, payload.words_16bit, &converted_len))
        {
            error = TRUE;
            break;
        }

        /* CAL Write: Common code to write to delta ACDB */
        error = deviceTestServiceCommand_SetCalibration(command->id, payload, converted_len, TRUE);
    }
    break;
        
    default:
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Write: Invalid command_id %u", command->id);
        error = TRUE;
    break;
    }

    if (error)
    {
        DeviceTestService_CommandResponseError(task);
    }
}

/*! \brief Handle the AT+CALGET
    \param task Identifier for the task that issued the command. This should be
                passed when responding to the command.
    \param[in] command Structure supplied by the AT command parser, providing
               the parameters passed in the AT command
*/
void DeviceTestServiceCommand_HandleAncV3Get(Task task,
    const struct DeviceTestServiceCommand_HandleAncV3Get* command)
{
    UNUSED(task);
    uint16 full_response_length = 0;

    if (!deviceTestService_AncCommandsAllowed() && ancv3_state.is_enabled)
    {
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Get: Invalid state %d %d",
            deviceTestService_AncCommandsAllowed(), ancv3_state.is_enabled);
        DeviceTestService_CommandResponseError(task);
        return;
    }

    if (command == NULL)
    {
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Get: command pointer is invalid");
        DeviceTestService_CommandResponseError(task);
        return;
    }

    switch (command->id)
    {
    /**** Internal version to track API changes */
    case ANCV3_COMMAND_ID_INTERNAL:
    {
        char response_buffer[FULL_ANCV3_GET_INTERNAL_LEN];
        full_response_length = FULL_ANCV3_GET_INTERNAL_LEN;
        memset(response_buffer, 0, sizeof(response_buffer));
        sprintf(response_buffer, DTS_ANCV3_GET_INTERNAL_RESPONSE DTS_ANCV3_CAL_VERSION);
        DeviceTestService_CommandResponse(task, response_buffer, full_response_length);
    }
    break;

    /**** Get Tuning mode */
    case ANCV3_COMMAND_ID_TUNING_MODE:
    /**** Get ANC mode */
    case ANCV3_COMMAND_ID_ANC_MODE:
    {
        char response_buffer[FULL_ANCV3_GET_ANC_MODE_RESPONSE_LEN];
        full_response_length = FULL_ANCV3_GET_ANC_MODE_RESPONSE_LEN;
        memset(response_buffer, 0, sizeof(response_buffer));
        uint16 anc_mode = appTestAudioCuration_GetCurrentMode();
        if (ancv3_state.is_tuning_mode)
        {
            DTS_LOG("DeviceTestServiceCommand_HandleAncV3Get: Tuning mode 0x%x", anc_mode);
            sprintf(response_buffer, DTS_ANCV3_GET_TUNING_MODE_RESPONSE "%x", anc_mode);
            DeviceTestService_CommandResponse(task, response_buffer, full_response_length);
        }
        else
        {
            DTS_LOG("DeviceTestServiceCommand_HandleAncV3Get: ANC mode 0x%x", anc_mode);
            sprintf(response_buffer, DTS_ANCV3_GET_ANC_MODE_RESPONSE "%x", anc_mode);
            DeviceTestService_CommandResponse(task, response_buffer, full_response_length);
        }
    }
    break;

    /**** CAL Get */
    case ANCV3_COMMAND_ID_CAL:
    {
        char response_buffer[FULL_ANCV3_GET_CAL_LEN];
        uint16 nr_of_chunks = 0;
        uint16 rest = 0;

        /* Get command payload: Convert ASCII payload to binary */
        ancv3_get_payload_buffer_t command_payload; /* buffer for binary converted command payload data */
        uint16 command_payload_len; 
        const uint8* command_payload_data_ptr = command->payload.data;
        memset(command_payload.words_16bit, 0, SIZE_IN_BYTES(MAX_ANCV3_GET_CHUNK_LENGTH_CAL));
        if (deviceTestServiceCommand_ConvertAsciiToBinary(command_payload_data_ptr,
            command->payload.length, ANCV3_MAX_CHUNK_LEN_GET, command_payload.words_16bit, &command_payload_len))
        {
            DeviceTestService_CommandResponseError(task);
            return;
        }

        /* First chunk received. Extract header */
        deviceTestServiceCommand_ExtractIDs(&command_payload.words_32bit, &ancv3_state.header);
        DTS_LOG("DeviceTestServiceCommand_HandleAncV3Get: Calibration: pid 0x%x", ancv3_state.header.pid);

        /* Allocate buffer for complete payload data to transfer */
        uint8* payload_bytes = malloc(SIZE_IN_BYTES(ancv3_state.header.total_payload_size));
        if (!payload_bytes)
        {
            DTS_LOG_PANIC("deviceTestServiceCommand_HandleAncV3Get: Memory allocation failed");
        }
        uint16* payload = payload_bytes;
        memset(payload_bytes, 0, SIZE_IN_BYTES(ancv3_state.header.total_payload_size));

        deviceTestServiceCommand_PrefillReceiveBuffer(&command_payload.words_32bit, command_payload_len, payload_bytes);
        DTS_LOG("DeviceTestServiceCommand_HandleAncV3Get: 0x%x 0x%x 0x%x 0x%x", payload_bytes[0], payload_bytes[1], payload_bytes[2], payload_bytes[3]);

        /* Read payload from capability */
        if (!appTestAudioCuration_ReadCapParam(ancv3_state.header.cap_id, 0,
                 ancv3_state.header.pid, SIZE_IN_BYTES(ancv3_state.header.total_payload_size), payload_bytes))
        {
            DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Get: Calibration: Reading cap_id 0x%x failed",
                          ancv3_state.header.cap_id);
            free(payload_bytes);
            break;
        }

        deviceTestServiceCommand_SwapPayload(payload_bytes, 0, SIZE_IN_32BIT(ancv3_state.header.total_payload_size));

        deviceTestServiceCommand_CalculateNumberOfChunks(ancv3_state.header.total_payload_size,
            MAX_ANCV3_GET_CHUNK_LENGTH_CAL, 0, &nr_of_chunks, &rest);

        /* Output all chunks */
        for (int ii = NUMBER2INDEX(nr_of_chunks); ii >= 0; ii--)
        {
            DTS_LOG("DeviceTestServiceCommand_HandleAncV3Get: Calibration: chunk_id %d", ii);
            uint16 payload_len = deviceTestServiceCommand_CalculateExpectedPayloadLen(nr_of_chunks, ii,
                                     MAX_ANCV3_GET_CHUNK_LENGTH_CAL, 0, rest);
            memset(response_buffer, 0, sizeof(response_buffer));
            sprintf(response_buffer, DTS_ANCV3_GET_CAL_RESPONSE);
            deviceTestServiceCommand_ConvertBinaryToAscii(
                &payload[(NUMBER2INDEX(nr_of_chunks) - ii) * MAX_ANCV3_GET_CHUNK_LENGTH_CAL],
                payload_len, response_buffer + sizeof(DTS_ANCV3_GET_CAL_RESPONSE) - 1);
            full_response_length = FULL_ANCV3_GET_CAL_LEN;
            DeviceTestService_CommandResponse(task, response_buffer, full_response_length);
        }
        free(payload_bytes);
    }
    break;

    default:
        DTS_LOG_ERROR("DeviceTestServiceCommand_HandleAncV3Get: Unknown command_id %d", command->id);
    break;
    }

    if (full_response_length > 0)
    {
        DeviceTestService_CommandResponseOk(task);
    }
    else
    {
        DeviceTestService_CommandResponseError(task);
    }
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncSetFineGain(Task task,
    const struct DeviceTestServiceCommand_HandleAncSetFineGain* ancSetFineGain)
{
    UNUSED(ancSetFineGain);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncReadFineGain(Task task,
    const struct DeviceTestServiceCommand_HandleAncReadFineGain* ancReadFineGain)
{
    UNUSED(ancReadFineGain);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncWriteFineGain(Task task,
    const struct DeviceTestServiceCommand_HandleAncWriteFineGain* ancWriteFineGain)
{
    UNUSED(ancWriteFineGain);
    DeviceTestService_CommandResponseError(task);
}


/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncSetFineGainDual(Task task,
    const struct DeviceTestServiceCommand_HandleAncSetFineGainDual* ancSetFineGainDual)
{
    UNUSED(ancSetFineGainDual);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncReadFineGainDual(Task task,
    const struct DeviceTestServiceCommand_HandleAncReadFineGainDual* ancReadFineGainDual)
{
    UNUSED(ancReadFineGainDual);
    DeviceTestService_CommandResponseError(task);
}


/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncWriteFineGainDual(Task task,
    const struct DeviceTestServiceCommand_HandleAncWriteFineGainDual* ancWriteFineGainDual)
{
    UNUSED(ancWriteFineGainDual);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncReadCoarseGain(Task task,
    const struct DeviceTestServiceCommand_HandleAncReadCoarseGain* ancReadCoarseGain)
{
    UNUSED(ancReadCoarseGain);
    DeviceTestService_CommandResponseError(task);
}

#else /*!ENABLE_ANC and !INCLUDE_ANC_V3 or BUILD_FOR_SAL */

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncDisable(Task task)
{
    UNUSED(task);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncEnable(Task task, 
                const struct DeviceTestServiceCommand_HandleAncEnable *ancEnable)
{
    UNUSED(ancEnable);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncSetFineGain(Task task, 
                    const struct DeviceTestServiceCommand_HandleAncSetFineGain *ancSetFineGain)
{
    UNUSED(ancSetFineGain);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncReadFineGain(Task task, 
                const struct DeviceTestServiceCommand_HandleAncReadFineGain *ancReadFineGain)
{
    UNUSED(ancReadFineGain);
    DeviceTestService_CommandResponseError(task);
}


/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncWriteFineGain(Task task,
                const struct DeviceTestServiceCommand_HandleAncWriteFineGain *ancWriteFineGain)
{
    UNUSED(ancWriteFineGain);
    DeviceTestService_CommandResponseError(task);
}


/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncSetFineGainDual(Task task,
                    const struct DeviceTestServiceCommand_HandleAncSetFineGainDual *ancSetFineGainDual)
{
    UNUSED(ancSetFineGainDual);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncReadFineGainDual(Task task,
                const struct DeviceTestServiceCommand_HandleAncReadFineGainDual *ancReadFineGainDual)
{
    UNUSED(ancReadFineGainDual);
    DeviceTestService_CommandResponseError(task);
}


/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncWriteFineGainDual(Task task,
                const struct DeviceTestServiceCommand_HandleAncWriteFineGainDual *ancWriteFineGainDual)
{
    UNUSED(ancWriteFineGainDual);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANC is disabled.*/
void DeviceTestServiceCommand_HandleAncReadCoarseGain(Task task,
                const struct DeviceTestServiceCommand_HandleAncReadCoarseGain *ancReadCoarseGain)
{
    UNUSED(ancReadCoarseGain);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANCV3 is disabled.*/
void DeviceTestServiceCommand_HandleAncV3Set(Task task,
    const struct DeviceTestServiceCommand_HandleAncV3Set *command)
{
    UNUSED(command);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANCV3 is disabled.*/
void DeviceTestServiceCommand_HandleAncV3Get(Task task,
    const struct DeviceTestServiceCommand_HandleAncV3Get* command)
{
    UNUSED(command);
    DeviceTestService_CommandResponseError(task);
}

/*! \brief Stub command included if ANCV3 is disabled.*/
void DeviceTestServiceCommand_HandleAncV3Write(Task task,
    const struct DeviceTestServiceCommand_HandleAncV3Write* command)
{
    UNUSED(command);
    DeviceTestService_CommandResponseError(task);
}

#endif /*!ENABLE_ANC and !INCLUDE_ANC_V3*/

/*! @} */
